﻿using System;
using Microsoft.SqlServer.Management.Smo;

namespace NazTek.Utility.Extensions.SqlObject
{
    /// <summary>
    /// Extends SMO Table's column object type.
    /// </summary>
    public static class ColumnExtension
    {
        /// <summary>
        /// Adds square brackets around data type name and maximum length in parenthesis for character data types.
        /// </summary>
        /// <param name="column">Provides the data type to inspect.</param>
        /// <returns>Data type name with length, when available.</returns>
        public static string GetDataTypeName(this Column column)
        {
            return column.DataType.Name.ToLower().Contains("char")
                ? string.Format("[{0}]({1})", column.DataType.Name, column.DataType.MaximumLength)
                : string.Format("[{0}]", column.DataType.Name);
        }
        /// <summary>
        /// Synchronizes a source column with a destination table, e.g., add it when missing 
        /// or modifies when data type of namesake column in the table differs.
        /// </summary>
        /// <param name="srcColumn">The column to use as model for comparison.</param>
        /// <param name="targetTable">The table to inspect columns of, for comparison.</param>
        /// <param name="publishNotice">Used to publish modification notice to client.</param>
        /// <returns>True when any modification has been done on <paramref name="targetTable"/>, otherwise false.</returns>
        public static bool Sync(this Column srcColumn, Table targetTable, Action<string> publishNotice)
        {
            Func<string, Action, bool> sync = (message, executeOperation) =>
            {
                publishNotice(string.Format(message, srcColumn.Serialize()));
                executeOperation();
                return true;
            };
            if (!targetTable.Columns.Contains(srcColumn.Name))
                return sync("Adding new column: {0}", () => srcColumn.AddColumn(targetTable));
            if (!targetTable.Columns[srcColumn.Name].IsEqualTo(srcColumn))
                return sync("Altering column to: {0}", () => targetTable.Columns[srcColumn.Name].UpdateColumn(srcColumn));
            return false;
        }

        /// <summary>
        /// Serializes the column with name, data type, and nullability.
        /// </summary>
        /// <param name="column">The columns to serialize.</param>
        /// <returns>Serialized column.</returns>
        private static string Serialize(this Column column)
        {
            return string.Format("[{0}] {1} {2}",
                column.Name, column.GetDataTypeName(), column.Nullable ? "NULL" : "NOT NULL");
        }
        /// <summary>
        /// Compares two columns for differences, by comparing their <see cref="Serialize">serialized</see> strings.
        /// </summary>
        /// <param name="srcColumn">Column 1 to compare.</param>
        /// <param name="dstColumn">Column 2 to compare.</param>
        /// <returns>True when the columns are equal, otherwise false.</returns>
        private static bool IsEqualTo(this Column srcColumn, Column dstColumn)
        {
            return (srcColumn.Serialize() == dstColumn.Serialize());
        }
        /// <summary>
        /// Adds a column to a table.
        /// </summary>
        /// <param name="srcColumn">The column to add.</param>
        /// <param name="targetTable">The table to add column to.</param>
        private static void AddColumn(this Column srcColumn, TableViewTableTypeBase targetTable)
        {
            targetTable.Columns.Add(new Column
            {
                Parent = targetTable,
                Name = srcColumn.Name,
                DataType = srcColumn.DataType,
                Nullable = srcColumn.Nullable
            });
        }
        /// <summary>
        /// Updates a column's length and nullability.
        /// </summary>
        /// <param name="dstColumn">The column to update.</param>
        /// <param name="srcColumn">The column to use a model for comparison for any update.</param>
        private static void UpdateColumn(this Column dstColumn, Column srcColumn)
        {
            if (srcColumn.DataType.Name.ToLower().Contains("char") &&
                srcColumn.DataType.MaximumLength != dstColumn.DataType.MaximumLength)
                dstColumn.DataType = srcColumn.DataType;
            if (srcColumn.Nullable != dstColumn.Nullable)
                dstColumn.Nullable = srcColumn.Nullable;
        }
    }
}